
// U7ObjViewerDlg.cpp : implementation file
//

#include "stdafx.h"
#include "U7ObjViewer.h"
#include "U7ObjViewerDlg.h"
#include "afxdialogex.h"

#include "U7FileDefs.h"
#include "Shape.h"
#include "MapChunk.h"

#include "U7Objs.inl"

#include "Renderer.h"
#include "Palette.h"

#include "FlexFile.h"

#include <algorithm>
#include <functional>
#include <atlconv.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

const int DEFAULT_Z_MAX_LEVEL = 4;

void DrawChunk( PGRAPHIC pGraphic, PSHAPEID shapeID, const char* shapeFile, int ox, int oy )
{
	for ( int i = 0; i < TILES_PER_CHUNK; i++ )
	{
		long shapeLength;
		PSHAPEENTRY shapeEntry = GetShape( shapeFile, shapeID->shapeType, &shapeLength );
		if ( IsShapeTile( shapeEntry, shapeLength ) )
		{
			int x = ox + (i%TILES_PER_CHUNK_IN_WIDTH)  * PIXELS_PER_TILE_IN_WIDTH  + ( PIXELS_PER_TILE_IN_WIDTH - 1 );
			int y = oy + (i/TILES_PER_CHUNK_IN_HEIGHT) * PIXELS_PER_TILE_IN_HEIGHT + ( PIXELS_PER_TILE_IN_HEIGHT - 1 );
			DrawShape( pGraphic, shapeEntry, shapeLength, shapeID->shapeFrame, x, y );
		}
		shapeID++;
	}
}

void DrawBase( U7FilesLoader* pFilesLoader, PGRAPHIC pGraphic, int ox, int oy )
{
	int startXTile = ox / PIXELS_PER_TILE_IN_WIDTH;
	int startYTile = oy / PIXELS_PER_TILE_IN_HEIGHT;
	int endXTile = (ox + pGraphic->width  + PIXELS_PER_TILE_IN_WIDTH  - 1) / PIXELS_PER_TILE_IN_WIDTH;
	int endYTile = (oy + pGraphic->height + PIXELS_PER_TILE_IN_HEIGHT - 1) / PIXELS_PER_TILE_IN_HEIGHT;

	int xAnchor = -(ox % PIXELS_PER_TILE_IN_WIDTH)  + (PIXELS_PER_TILE_IN_WIDTH  - 1);
	int yAnchor = -(oy % PIXELS_PER_TILE_IN_HEIGHT) + (PIXELS_PER_TILE_IN_HEIGHT - 1);

	for ( int yt = startYTile; yt < endYTile; yt++ )
	{
		int xpos = xAnchor;

		for ( int xt = startXTile; xt < endXTile; xt++ )
		{
			int xchunk = xt / TILES_PER_CHUNK_IN_WIDTH;
			int ychunk = yt / TILES_PER_CHUNK_IN_HEIGHT;
			int xchunk_x = xt % TILES_PER_CHUNK_IN_WIDTH;
			int ychunk_y = yt % TILES_PER_CHUNK_IN_HEIGHT;
			int xschunk = xchunk / CHUNKS_PER_SUPERCHUNK_IN_WIDTH;
			int yschunk = ychunk / CHUNKS_PER_SUPERCHUNK_IN_HEIGHT;
			int xschunk_x = xchunk % CHUNKS_PER_SUPERCHUNK_IN_WIDTH;
			int yschunk_y = ychunk % CHUNKS_PER_SUPERCHUNK_IN_HEIGHT;

			MAP map = (MAP)pFilesLoader->mapFile;
			PATTERN_ID chunkID = map[yschunk][xschunk][yschunk_y][xschunk_x];

			/*PCHUNKID pChunkID = (PCHUNKID)pFilesLoader->mapFile
				+ CHUNKS_PER_SUPERCHUNK * (yschunk * SUPERCHUNKS_PER_MAP_IN_WIDTH + xschunk )
				+ yschunk_y * CHUNKS_PER_SUPERCHUNK_IN_WIDTH + xschunk_x;*/

			CHUNK_PATTERN chunkPattern = (CHUNK_PATTERN)pFilesLoader->chunkFile;
			SHAPEID (*pattern)[TILES_PER_CHUNK_IN_WIDTH] = chunkPattern[chunkID];
			SHAPEID shapeID = pattern[ychunk_y][xchunk_x];

			/*PSHAPEID pShapeID = (PSHAPEID)pFilesLoader->chunkFile
				+ TILES_PER_CHUNK * (*pChunkID)
				+ ychunk_y * TILES_PER_CHUNK_IN_WIDTH + xchunk_x;*/

			long shapeLength;
			PSHAPEENTRY shapeEntry = GetShape( pFilesLoader->shapeFile, shapeID.shapeType, &shapeLength );
			if ( IsShapeTile( shapeEntry, shapeLength ) )
			{
				DrawShape( pGraphic, shapeEntry, shapeLength, shapeID.shapeFrame, xpos, yAnchor );
			}

			xpos += PIXELS_PER_TILE_IN_WIDTH;
		}

		yAnchor += PIXELS_PER_TILE_IN_HEIGHT;
	}
}

void DrawMap( PGRAPHIC pGraphic, int ox, int oy, WorldMap& map, int zmax )
{
	Renderer renderer;

	renderer.Paint( pGraphic, ox, oy, map, zmax );
}

// CAboutDlg dialog used for App About

class CAboutDlg : public CDialogEx
{
public:
	CAboutDlg();

// Dialog Data
	enum { IDD = IDD_ABOUTBOX };

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

// Implementation
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CU7ObjViewerDlg dialog

class PaletteRotation : public ITimeAware
{
	TimeQueue* tqueue;
public:
	PaletteRotation( TimeQueue* tq ) : tqueue(tq) {}
	virtual void HandleEvent(unsigned long time, void* context)
	{
		// add to queue again
		tqueue->Add( time + 200 * 1000, this, context );
		GRAPHIC** g = (GRAPHIC**)context;
		if (!*g) return;
		RotateGraphicPalette( *g, 0xFC, 3 );
		RotateGraphicPalette( *g, 0xF8, 4 );
		RotateGraphicPalette( *g, 0xF4, 4 );
		RotateGraphicPalette( *g, 0xF0, 4 );
		RotateGraphicPalette( *g, 0xE8, 8 );
		RotateGraphicPalette( *g, 0xE0, 8 );
	}
};

CU7ObjViewerDlg::CU7ObjViewerDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CU7ObjViewerDlg::IDD, pParent), m_map(&m_FilesLoader,&m_TQueue), m_dwTime(0)
{
	static PaletteRotation sPaletteRotation(&m_TQueue);

	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

	m_bIsPaused = false;

	m_TQueue.Add( 0, &sPaletteRotation, &m_pGraphic );

	m_pGraphic = NULL;

	m_ptDisplayOrigin.x = AfxGetApp()->GetProfileInt( _T("Settings"), _T("DisplayX"), 0 );
	m_ptDisplayOrigin.y = AfxGetApp()->GetProfileInt( _T("Settings"), _T("DisplayY"), 0 );
	m_strRootPath = AfxGetApp()->GetProfileString( _T("Settings"), _T("U7RootPath"), _T("") );

	m_ptNewDisplayOrigin = m_ptDisplayOrigin;

	m_bLButtonCaptured = false;
	m_bRButtonCaptured = false;

	m_selectedObject = NULL;

	m_curShapeEntry = NULL;
	m_nCurFrameIndex = -1;

	m_viewZMax = DEFAULT_Z_MAX_LEVEL;
}

void CU7ObjViewerDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_STT_FRAME, m_sttFrame);
	DDX_Control(pDX, IDC_LC_OBJ, m_lcObj);
	DDX_Control(pDX, IDC_LC_SHAPE, m_lcShapeFrame);
	DDX_Control(pDX, IDC_LC_SHAPE_INFO, m_lcShapeInfo);
	DDX_Control(pDX, IDC_LC_OBJ_INFO, m_lcSelObjInfo);
	DDX_Control(pDX, IDC_SLIDER_Z_MAX, m_scZMax);
}

BEGIN_MESSAGE_MAP(CU7ObjViewerDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_WM_MOVE()
	ON_WM_SIZE()
	ON_WM_GETMINMAXINFO()
	ON_BN_CLICKED(IDCANCEL, &CU7ObjViewerDlg::OnBnClickedCancel)
	ON_NOTIFY(LVN_ITEMCHANGED, IDC_LC_OBJ, &CU7ObjViewerDlg::OnLvnItemchangedLcObj)
	ON_NOTIFY(LVN_ITEMCHANGED, IDC_LC_SHAPE, &CU7ObjViewerDlg::OnLvnItemchangedLcShape)
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_MOUSEMOVE()
	ON_WM_RBUTTONDOWN()
	ON_WM_RBUTTONUP()
	ON_WM_VSCROLL()
	ON_COMMAND(ID_FILE_EXIT, &CU7ObjViewerDlg::OnFileExit)
	ON_COMMAND(ID_FILE_LOAD, &CU7ObjViewerDlg::OnFileLoad)
END_MESSAGE_MAP()


// CU7ObjViewerDlg message handlers

BOOL CU7ObjViewerDlg::OnInitDialog()
{
	USES_CONVERSION;

	CDialogEx::OnInitDialog();

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon

	// TODO: Add extra initialization here
	/*
	PFLEXFILEHEADER h = CreateFlexFile();
	h = AppendFlexEntity( h, "abcd", 5 );
	h = AppendFlexEntity( h, "xyz", 4 );

	PFLEXFILEENTRY e0 = GetFlexEntry(h,0);
	PFLEXFILEENTRY e1 = GetFlexEntry(h,1);

	char* ent0 = GetFlexEntity(h,0);
	char* ent1 = GetFlexEntity(h,1);

	DestroyFlexFile(h);
	*/



	BEGIN_OBJ_MAP(CU7ObjViewerDlg);
		OBJ_DEFINE(IDC_LC_OBJ,			0,   0,   0, 100 );
		OBJ_DEFINE(IDC_STT_FRAME,		0, 100,   0, 100 );
		OBJ_DEFINE(IDC_SLIDER_Z_MAX,  100, 100,   0, 100 );
		OBJ_DEFINE(IDC_LC_SHAPE_INFO,	0,   0, 100, 100 );
		OBJ_DEFINE(IDC_LC_OBJ_INFO,		0,   0, 100, 100 );
		OBJ_DEFINE(IDC_LC_SHAPE,		0, 100, 100, 100 );
	END_OBJ_MAP(CU7ObjViewerDlg);
	/*
	_apObjInfo_[0].rc.right += 64;
	_apObjInfo_[1].rc.left += 64;
	_apObjInfo_[2].rc.left += 64;

	CRect rcClient;
	GetClientRect(&rcClient);
	UPDATE_OBJ_POSITION(rcClient.Width(),rcClient.Height());
	*/

	LoadFiles();

	PFLEXFILEHEADER hdr = (PFLEXFILEHEADER)m_FilesLoader.gumpFile;
	int count = hdr->count;

	// create graphic
	CRect rc;
	m_sttFrame.GetClientRect(&rc);
	if ( m_pGraphic )
	{
		DeleteGraphic(m_pGraphic);
	}
	m_pGraphic = CreateGraphic( rc.Width(), rc.Height(), 8 );

	// handle palette
	if ( m_FilesLoader.paletteFile )
	{
		RGBQUAD* bmiColors = ((PBITMAPINFO)m_bitmapInfo)->bmiColors;
		// copy the first palette to the global one
		GetU7Palette( m_FilesLoader.paletteFile, 0, bmiColors );

		SetGraphicPalette( m_FilesLoader.paletteFile, PALETTE_DAY, m_pGraphic );
	}

#if 0
	// the code below will create a file Minimap.cpp under d:\.....
	CreateMiniMap();
#endif

	// obj list control
	m_lcObj.InsertColumn( 0, _T("NO.") );
	m_lcObj.InsertColumn( 1, _T("Name") );
	if ( m_FilesLoader.textFile )
	{
		PFLEXFILEHEADER pHeader = (PFLEXFILEHEADER)m_FilesLoader.textFile;
		CString s;
		for ( int i = 0; i < 1024; i++ )
		{
			s.Format( _T("%d"), i );
			m_lcObj.InsertItem( i, s );

			PFLEXFILEENTRY entry = (PFLEXFILEENTRY)(pHeader+1) + i;
			ASSERT( entry->offset && entry->size );

			m_lcObj.SetItemText( i, 1, A2T((char*)m_FilesLoader.textFile + entry->offset) );
		}
	}
	int nNumColumns = m_lcObj.GetHeaderCtrl()->GetItemCount();
	for ( int col = 0; col < nNumColumns; col++ )
	{
		m_lcObj.SetColumnWidth(col,LVSCW_AUTOSIZE_USEHEADER);
	}
	m_lcObj.SetExtendedStyle(LVS_EX_GRIDLINES|LVS_EX_FULLROWSELECT);

	// shape info list control
	m_lcShapeInfo.InsertColumn( 0, _T("Property") );
	m_lcShapeInfo.InsertColumn( 1, _T("Value") );

	nNumColumns = m_lcShapeInfo.GetHeaderCtrl()->GetItemCount();
	for ( int col = 0; col < nNumColumns; col++ )
	{
		m_lcShapeInfo.SetColumnWidth(col,LVSCW_AUTOSIZE_USEHEADER);
	}

	m_lcShapeInfo.SetExtendedStyle(LVS_EX_GRIDLINES|LVS_EX_FULLROWSELECT);

	// selected obj info list control
	m_lcSelObjInfo.InsertColumn( 0, _T("Property") );
	m_lcSelObjInfo.InsertColumn( 1, _T("Value") );
	m_lcSelObjInfo.InsertItem( 0, _T("x tiles") );
	m_lcSelObjInfo.InsertItem( 1, _T("y tiles") );
	m_lcSelObjInfo.InsertItem( 2, _T("z level") );
	m_lcSelObjInfo.InsertItem( 3, _T("x chunk") );
	m_lcSelObjInfo.InsertItem( 4, _T("y chunk") );
	m_lcSelObjInfo.InsertItem( 5, _T("c++ class") );
	m_lcSelObjInfo.InsertItem( 6, _T("ireg") );
	nNumColumns = m_lcSelObjInfo.GetHeaderCtrl()->GetItemCount();
	for ( int col = 0; col < nNumColumns; col++ )
	{
		m_lcSelObjInfo.SetColumnWidth(col,LVSCW_AUTOSIZE_USEHEADER);
	}
	m_lcSelObjInfo.SetExtendedStyle(LVS_EX_GRIDLINES|LVS_EX_FULLROWSELECT);

	// z slider control
	m_scZMax.SetRange( -2, 15 );
	m_scZMax.SetPos( 13 - m_viewZMax );
	m_scZMax.SetTic(14); // -1
	m_scZMax.SetTic(13); //  0
	m_scZMax.SetTic(9);  //  4
	m_scZMax.SetTic(4);  //  9
	m_scZMax.SetTic(3);  // 10

	// start animation thread
	Start();

	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CU7ObjViewerDlg::CreateMiniMap()
{
	unsigned char* mapping = new unsigned char[3072];

	PGRAPHIC tmpG = CreateGraphic( PIXELS_PER_CHUNK_IN_WIDTH, PIXELS_PER_CHUNK_IN_HEIGHT, 8, ((PBITMAPINFO)m_bitmapInfo)->bmiColors );
	if ( tmpG )
	{
		RGBQUAD* rgb =((PBITMAPINFO)m_bitmapInfo)->bmiColors;
		int area = tmpG->width * tmpG->height;

		for ( int pat = 0; pat < 3072; pat++ )
		{
			int r = 0, g = 0, b = 0;

			Renderer renderer;
			renderer.PaintTerrainTiles( tmpG, m_FilesLoader, pat );
			PBYTE pStart = (PBYTE)tmpG->pBits;
			for ( int y = 0; y < tmpG->height; y++ )
			{
				PBYTE p = pStart;
				std::for_each( p, p + tmpG->width,
					[&]( BYTE idx ) {
						r += rgb[idx].rgbRed;
						g += rgb[idx].rgbGreen;
						b += rgb[idx].rgbBlue;
				});
				pStart += tmpG->pitch;
			}
			r /= area;
			g /= area;
			b /= area;
			mapping[pat] = FindColor( rgb, 224, r, g, b );
		}
		DeleteGraphic(tmpG);
	}

	// create the form of the shape frame
	FILE* fp;
	fopen_s( &fp, "d:\\Minimap.cpp", "wt" );
	fprintf( fp, "\n\nunsigned char gMinimap[] = {\n" );
	fprintf( fp, "0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x00,\n" ); // right, left, top bottom

	for ( int y = 0; y < CHUNKS_PER_MAP_IN_HEIGHT; y++ )
	{
		fprintf( fp, "0x80, 0x01, 0x00, 0x00, 0x%02x, 0x%02x, ", y % 256, y / 256 );
		
		for ( int x = 0; x < CHUNKS_PER_MAP_IN_WIDTH; x++ )
		{
			int yschk = y / CHUNKS_PER_SUPERCHUNK_IN_HEIGHT;
			int xschk = x / CHUNKS_PER_SUPERCHUNK_IN_WIDTH;
			int ychk = y % CHUNKS_PER_SUPERCHUNK_IN_HEIGHT;
			int xchk = x % CHUNKS_PER_SUPERCHUNK_IN_WIDTH;

			PATTERN_ID pid = ((MAP)m_FilesLoader.mapFile)[yschk][xschk][ychk][xchk];
			fprintf( fp, "0x%02x, ", mapping[pid] );
		}
		fprintf( fp, "\n" );
	}

	fprintf( fp, "0x00, 0x00\n};\n\n" );

	fclose(fp);
	delete mapping;
}

void CU7ObjViewerDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialogEx::OnSysCommand(nID, lParam);
	}
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CU7ObjViewerDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		if ( !m_pGraphic )
		{
			CDialogEx::OnPaint();
		}
		else
		{
			CRect rc;
			CPaintDC dc(this);
			/*
			m_sttFrame.GetWindowRect(&rc);
			ScreenToClient(&rc);

			CDC dcMem;
			dcMem.CreateCompatibleDC(&dc);
			CBitmap* pOldBitmap = dcMem.SelectObject( CBitmap::FromHandle(m_pGraphic->hBitmap) );
#if DOUBLE
			dc.StretchBlt( rc.left, rc.top, m_pGraphic->width, m_pGraphic->height,
				&dcMem, m_pGraphic->width/4, m_pGraphic->height/4, m_pGraphic->width/2, m_pGraphic->height/2,
				SRCCOPY );
#else
			dc.BitBlt( rc.left, rc.top, m_pGraphic->width, m_pGraphic->height,
				&dcMem, 0, 0, SRCCOPY );
#endif

			dcMem.SelectObject(pOldBitmap);
			*/
		}
	}
}

// The system calls this function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CU7ObjViewerDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

void CU7ObjViewerDlg::OnMove( int x, int y )
{
	SAVE_WINDOW_PLACEMENT(CU7ObjViewerDlg);
}

void CU7ObjViewerDlg::OnSize(UINT nType, int cx, int cy)
{
	CDialogEx::OnSize(nType, cx, cy);
	UPDATE_OBJ_POSITION(cx,cy);
	SAVE_WINDOW_PLACEMENT(CU7ObjViewerDlg);
	/*
	if ( nType == SIZE_MINIMIZED )
	{
		m_bIsPaused = true;
		return;
	}
	else if ( nType == SIZE_RESTORED )
	{
		m_bIsPaused = false;
	}
	*/
	if ( m_sttFrame.GetSafeHwnd() && m_FilesLoader.paletteFile )
	{
		m_CritSection.Lock();

		CRect rc;
		m_sttFrame.GetClientRect(&rc);
		
		if ( m_pGraphic )
		{
			DeleteGraphic(m_pGraphic);
		}
		
		m_pGraphic = CreateGraphic( rc.Width(), rc.Height(), 8, ((const BITMAPINFO *)m_bitmapInfo)->bmiColors );

		m_CritSection.Unlock();
	}
}

void CU7ObjViewerDlg::OnGetMinMaxInfo(MINMAXINFO* lpMMI)
{
	UPDATE_MINMAX_INFO(lpMMI);
	CDialogEx::OnGetMinMaxInfo(lpMMI);
}

void CU7ObjViewerDlg::OnBnClickedCancel()
{
	// TODO: Add your control notification handler code here
	CDialogEx::OnCancel();

	// stop animation thread
	Stop();

	if ( m_pGraphic )
	{
		DeleteGraphic(m_pGraphic);
		m_pGraphic = NULL;
	}

	Renderer::FreeResources();

	AfxGetApp()->WriteProfileInt( _T("Settings"), _T("DisplayX"), m_ptDisplayOrigin.x );
	AfxGetApp()->WriteProfileInt( _T("Settings"), _T("DisplayY"), m_ptDisplayOrigin.y );

}

void CU7ObjViewerDlg::LoadFiles()
{
	USES_CONVERSION;

	if ( m_strRootPath.IsEmpty() )
	{
		m_strRootPath = _T("D:\\Private\\temp\\DOSBox-0.73\\cdrive\\U7\\");
	}

	MessageBox( m_strRootPath, _T("U7 File Directory"), MB_OK );

	m_FilesLoader.Load(T2A((LPCTSTR)m_strRootPath));
}

void CU7ObjViewerDlg::OnLvnItemchangedLcObj(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
	// TODO: Add your control notification handler code here
	*pResult = 0;

	m_imageList.DeleteImageList();
	m_lcShapeFrame.DeleteAllItems();

	if ( pNMLV->iItem < 0 || !m_FilesLoader.shapeFile )
		return;

	int nObjIndex = pNMLV->iItem;

	long shapeLength;
	PSHAPEENTRY shapeEntry = GetShape( m_FilesLoader.shapeFile, nObjIndex, &shapeLength );
	int frameCount = GetShapeFrameCount( shapeEntry, shapeLength );
	int maxW = 0, maxH = 0;
	for ( int i = 0; i < frameCount; i++ )
	{
		U7VECTOR2 hotspot;
		U7VECTOR2 dimension;
		GetShapeFrameInfo( shapeEntry, shapeLength, i, hotspot, dimension );
		if ( dimension.x > maxW )
			maxW = dimension.x;
		if ( dimension.y > maxH )
			maxH = dimension.y;
	}
		
	m_imageList.Create( maxW, maxH, ILC_COLOR24, frameCount, 0 ); // the result is same for ILC_COLOR8 and ILC_COLOR24

	PGRAPHIC tmpG = CreateGraphic( maxW, maxH, 8, ((const BITMAPINFO *)m_bitmapInfo)->bmiColors );

	for ( int i = 0; i < frameCount; i++ )
	{
		U7VECTOR2 hotspot;
		U7VECTOR2 dimension;
		GetShapeFrameInfo( shapeEntry, shapeLength, i, hotspot, dimension );

		DrawShape( tmpG, shapeEntry, shapeLength, i, hotspot.x, hotspot.y );

		int idx = m_imageList.Add( CBitmap::FromHandle(tmpG->hBitmap), RGB(0,0,0) );

		ClearGraphic(tmpG);
	}

	DeleteGraphic(tmpG);

	// shape list view
	m_lcShapeFrame.SetImageList( &m_imageList, LVSIL_NORMAL );
	CString s;
	CPoint pt;
	CRect rc(0,0,0,0);
	for ( int i = 0; i < frameCount; i++ )
	{
		U7VECTOR2 hotspot;
		U7VECTOR2 dimension;
		GetShapeFrameInfo( shapeEntry, shapeLength, i, hotspot, dimension );

		s.Format( _T("%d_%d\n(%d,%d)\n(%d,%d)"),
			nObjIndex, i,
			hotspot.x, hotspot.y,
			dimension.x, dimension.y );
		m_lcShapeFrame.InsertItem( i, s, i );

		if ( rc.IsRectEmpty() )
		{
			m_lcShapeFrame.GetItemPosition( i,&pt );
			m_lcShapeFrame.GetSubItemRect( i, 0, LVIR_BOUNDS, rc );
		}
		else
		{
			m_lcShapeFrame.SetItemPosition( i, CPoint( pt.x + rc.Width() * i, pt.y ) );
		}
	}

	// info list view
	int curRow = 0;
	m_lcShapeInfo.DeleteAllItems();
	if ( m_FilesLoader.shpDimsFile && nObjIndex >= 0x96 )
	{
		PSHIPDIMS pShpDims = (PSHIPDIMS)m_FilesLoader.shpDimsFile;
		pShpDims += (nObjIndex - 0x96);

		m_lcShapeInfo.InsertItem( curRow, _T("ShpDims") ); 
		
		CString s;
		s.Format( _T("(%d,%d),[%d,%d]"),
			pShpDims->xdims, pShpDims->ydims,
			pShpDims->xobstacle, pShpDims->yobstacle );
		m_lcShapeInfo.SetItemText( curRow, 1, s );
		curRow++;
	}
	if ( m_FilesLoader.tfaFile )
	{
		PTFA pTfa = (PTFA)m_FilesLoader.tfaFile;
		pTfa += nObjIndex;

		CString s;
		m_lcShapeInfo.InsertItem( curRow, _T("xyz tiles") );
		s.Format( _T("%d, %d, %d"),
			pTfa->xtiles + 1, pTfa->ytiles + 1, pTfa->ztiles );
		m_lcShapeInfo.SetItemText( curRow++, 1, s );

		m_lcShapeInfo.InsertItem( curRow, _T("sound_effect") );
		s.Format( _T("%s"), pTfa->sound_effect ? _T("TRUE") : _T("FALSE") );
		m_lcShapeInfo.SetItemText( curRow++, 1, s );

		m_lcShapeInfo.InsertItem( curRow, _T("strange_movement") );
		s.Format( _T("%s"), pTfa->strange_movement ? _T("TRUE") : _T("FALSE") );
		m_lcShapeInfo.SetItemText( curRow++, 1, s );

		m_lcShapeInfo.InsertItem( curRow, _T("animated") );
		s.Format( _T("%s"), pTfa->animated ? _T("TRUE") : _T("FALSE") );
		m_lcShapeInfo.SetItemText( curRow++, 1, s );

		m_lcShapeInfo.InsertItem( curRow, _T("solid") );
		s.Format( _T("%s"), pTfa->solid ? _T("TRUE") : _T("FALSE") );
		m_lcShapeInfo.SetItemText( curRow++, 1, s );

		m_lcShapeInfo.InsertItem( curRow, _T("water") );
		s.Format( _T("%s"), pTfa->water ? _T("TRUE") : _T("FALSE") );
		m_lcShapeInfo.SetItemText( curRow++, 1, s );

		m_lcShapeInfo.InsertItem( curRow, _T("field") );
		s.Format( _T("%s"), pTfa->field ? _T("TRUE") : _T("FALSE") );
		m_lcShapeInfo.SetItemText( curRow++, 1, s );

		m_lcShapeInfo.InsertItem( curRow, _T("door") );
		s.Format( _T("%s"), pTfa->door ? _T("TRUE") : _T("FALSE") );
		m_lcShapeInfo.SetItemText( curRow++, 1, s );

		m_lcShapeInfo.InsertItem( curRow, _T("barge_part") );
		s.Format( _T("%s"), pTfa->barge_part ? _T("TRUE") : _T("FALSE") );
		m_lcShapeInfo.SetItemText( curRow++, 1, s );

		m_lcShapeInfo.InsertItem( curRow, _T("translucent") );
		s.Format( _T("%s"), pTfa->translucent ? _T("TRUE") : _T("FALSE") );
		m_lcShapeInfo.SetItemText( curRow++, 1, s );

		m_lcShapeInfo.InsertItem( curRow, _T("light_source") );
		s.Format( _T("%s"), pTfa->light_source ? _T("TRUE") : _T("FALSE") );
		m_lcShapeInfo.SetItemText( curRow++, 1, s );

		m_lcShapeInfo.InsertItem( curRow, _T("include translucent color") );
		s.Format( _T("%s"), pTfa->containsTransparencyColors ? _T("TRUE") : _T("FALSE") );
		m_lcShapeInfo.SetItemText( curRow++, 1, s );

		m_lcShapeInfo.InsertItem( curRow, _T("shape class") );
		s.Format( _T("%d"), pTfa->shapeClass );
		m_lcShapeInfo.SetItemText( curRow++, 1, s );
	}
	if ( m_FilesLoader.occludeFile )
	{
		char val = m_FilesLoader.occludeFile[nObjIndex / 8] & ( 1 << (nObjIndex%8) );

		CString s;
		m_lcShapeInfo.InsertItem( curRow, _T("occlude") );
		s.Format( _T("%s"), val ? _T("true") : _T("false") );
		m_lcShapeInfo.SetItemText( curRow++, 1, s );
	}

}

void CU7ObjViewerDlg::OnLvnItemchangedLcShape(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
	// TODO: Add your control notification handler code here
	*pResult = 0;

	if ( pNMLV->iItem < 0 )
		return;

	POSITION pos = m_lcObj.GetFirstSelectedItemPosition();
	ASSERT(pos);

	int nObjIndex = m_lcObj.GetNextSelectedItem(pos);
	//m_curShapeEntry = GetShape( m_FilesLoader.shapeFile, nObjIndex, m_curShapeLength );
	//m_nCurFrameIndex = pNMLV->iItem;

	PTFA pTfa = (PTFA)m_FilesLoader.tfaFile;
	pTfa += nObjIndex;
	m_bAnimated = pTfa->animated;
}

void CU7ObjViewerDlg::UpdateGame()
{
	if ( m_bIsPaused )
		return;

	m_dwTime += m_FRAME_INTERVAL;

	m_ptDisplayOrigin = m_ptNewDisplayOrigin;

	m_CritSection.Lock();

	m_TQueue.Update(m_dwTime);

	m_map.ReadData( m_ptDisplayOrigin.x, m_ptDisplayOrigin.y,
		m_pGraphic->width, m_pGraphic->height );

	m_CritSection.Unlock();
}

void CU7ObjViewerDlg::RenderGame()
{
	extern unsigned char gMinimap[];

	if ( m_bIsPaused )
		return;

	m_CritSection.Lock();

	ClearGraphic( m_pGraphic );

	//DrawBase( &m_FilesLoader, m_pGraphic, m_ptDisplayOrigin.x, m_ptDisplayOrigin.y );
	DrawMap(m_pGraphic, m_ptDisplayOrigin.x, m_ptDisplayOrigin.y, m_map, m_viewZMax);

	// draw mini-map
	if ( m_pGraphic->width > 193 && m_pGraphic->height > 193 )
	{
		m_bMinmapVisible = true;
		m_rcMinimap.left = m_pGraphic->width - 193;
		m_rcMinimap.right = m_rcMinimap.left + 192;
		m_rcMinimap.top = m_pGraphic->height - 193;
		m_rcMinimap.bottom = m_rcMinimap.top + 192;

		PFRAMEENTRY pMinimap = (PFRAMEENTRY)gMinimap;
		DrawShapeFrame( m_pGraphic, pMinimap,
			m_rcMinimap.left,
			m_rcMinimap.top );

		int xcross = (m_ptDisplayOrigin.x + m_pGraphic->width / 2) / PIXELS_PER_CHUNK_IN_WIDTH;
		int ycross = (m_ptDisplayOrigin.y + m_pGraphic->height / 2 ) / PIXELS_PER_CHUNK_IN_HEIGHT;

		xcross %= CHUNKS_PER_MAP_IN_WIDTH;
		ycross %= CHUNKS_PER_MAP_IN_HEIGHT;

		char* pcross = (char*)m_pGraphic->pBits
			+ (m_rcMinimap.top + ycross) * m_pGraphic->pitch
			+ (m_rcMinimap.left + xcross);
		*pcross = 0;
		*(pcross-1) = 0;
		*(pcross+1) = 0;
		*(pcross-m_pGraphic->pitch) = 0;
		*(pcross+m_pGraphic->pitch) = 0;
	}
	else
		m_bMinmapVisible = false;

	//
	// obsolete
	//
	if ( m_curShapeEntry == NULL || m_nCurFrameIndex < 0 )
	{
		m_CritSection.Unlock();//important
		return;
	}

	U7VECTOR2 hotspot;
	U7VECTOR2 dimension;
	GetShapeFrameInfo( m_curShapeEntry, m_curShapeLength, m_nCurFrameIndex, hotspot, dimension );

	int anchorX = m_pGraphic->width / 2;
	int anchorY = m_pGraphic->height/ 2;

	DrawShape( m_pGraphic, m_curShapeEntry, m_curShapeLength, m_nCurFrameIndex, anchorX, anchorY );

	m_CritSection.Unlock();
}

void CU7ObjViewerDlg::PaintScreen()
{
	if ( m_bIsPaused )
		return;

	m_CritSection.Lock();

	CRect rc;
	CClientDC dc(this);
	m_sttFrame.GetWindowRect(&rc);
	ScreenToClient(&rc);

	CDC dcMem;
	dcMem.CreateCompatibleDC(&dc);
	CBitmap* pOldBitmap = dcMem.SelectObject( CBitmap::FromHandle(m_pGraphic->hBitmap) );
#if DOUBLE
	dc.StretchBlt( rc.left, rc.top, m_pGraphic->width, m_pGraphic->height,
		&dcMem, m_pGraphic->width/4, m_pGraphic->height/4, m_pGraphic->width/2, m_pGraphic->height/2,
		SRCCOPY );
#else
	dc.BitBlt( rc.left, rc.top, m_pGraphic->width, m_pGraphic->height,
		&dcMem, 0, 0, SRCCOPY );
#endif

	dcMem.SelectObject(pOldBitmap);


	dc.SelectStockObject( WHITE_PEN );
	dc.SelectStockObject( DKGRAY_BRUSH );

	int nCurrentTextY = rc.top;

	{
		double fps = GetAverageFPS();
		double ups = GetAverageUPS();
		CString s;
		s.Format( _T("FPS%.2f/UPS%.2f, (%d,%d)"),
			fps, ups,
			m_ptDisplayOrigin.x, m_ptDisplayOrigin.y );
		dc.TextOut( rc.left, nCurrentTextY, s );
		CSize szTextExt = dc.GetTextExtent(s);
		nCurrentTextY += szTextExt.cy + 1;
	}

	CPoint cursor;
	GetCursorPos(&cursor);
	ScreenToClient(&cursor);

	if ( rc.PtInRect(cursor) )
	{
		CPoint pt = m_ptDisplayOrigin + (cursor - rc.TopLeft());
		pt.x %= PIXELS_PER_MAP_IN_WIDTH;
		pt.y %= PIXELS_PER_MAP_IN_HEIGHT;
		int xchk = pt.x / PIXELS_PER_CHUNK_IN_WIDTH;
		int ychk = pt.y / PIXELS_PER_CHUNK_IN_HEIGHT;
		int xtile = (pt.x - xchk * PIXELS_PER_CHUNK_IN_WIDTH)/PIXELS_PER_TILE_IN_WIDTH;
		int ytile = (pt.y - ychk * PIXELS_PER_CHUNK_IN_HEIGHT)/PIXELS_PER_TILE_IN_HEIGHT;
		CString s;
		s.Format( _T("px(%d,%d) chk(%d,%d) t(%d,%d)"), pt.x, pt.y, xchk, ychk, xtile, ytile );
		dc.TextOut( rc.left, nCurrentTextY, s );
		CSize szTextExt = dc.GetTextExtent(s);
		nCurrentTextY += szTextExt.cy + 1;
	}

/*

	CRgn rgn;
	rgn.CreateRectRgn( rc.left, rc.top, rc.right, rc.bottom );
	dc.SelectClipRgn(&rgn);
	dc.MoveTo( cursor.x-16, cursor.y );
	dc.LineTo( cursor.x+16, cursor.y );
	dc.MoveTo( cursor.x, cursor.y-16 );
	dc.LineTo( cursor.x, cursor.y+16 );
	dc.MoveTo( cursor.x, cursor.y );
	dc.LineTo( cursor.x-32, cursor.y-32 );
*/
	m_CritSection.Unlock();
}



void CU7ObjViewerDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
	// TODO: Add your message handler code here and/or call default
	__super::OnLButtonDown(nFlags, point);

	CRect rc;
	m_sttFrame.GetWindowRect(&rc);
	ScreenToClient(&rc);

	if ( rc.PtInRect(point) && !m_bRButtonCaptured )
	{
		if ( m_bMinmapVisible )
		{
			CRect mini(m_rcMinimap);
			mini += rc.TopLeft();
			if ( mini.PtInRect(point) )
			{
				CSize sz = point - mini.TopLeft();
				m_CritSection.Lock();
				m_ptNewDisplayOrigin.x = sz.cx * PIXELS_PER_CHUNK_IN_WIDTH + PIXELS_PER_CHUNK_IN_WIDTH/2 - rc.Width()/2;
				m_ptNewDisplayOrigin.y = sz.cy * PIXELS_PER_CHUNK_IN_HEIGHT + PIXELS_PER_CHUNK_IN_HEIGHT/2 - rc.Height()/2;
				m_ptNewDisplayOrigin.x = (m_ptNewDisplayOrigin.x+PIXELS_PER_MAP_IN_WIDTH) % PIXELS_PER_MAP_IN_WIDTH;
				m_ptNewDisplayOrigin.y = (m_ptNewDisplayOrigin.y+PIXELS_PER_MAP_IN_HEIGHT) % PIXELS_PER_MAP_IN_HEIGHT;
				m_CritSection.Unlock();
				return;
			}
		}

		SetCapture();
		m_bLButtonCaptured = true;
		m_ptLastCaptured = point;
		m_ptLastDisplayOrigin = m_ptDisplayOrigin;

		CSize sz = point - rc.TopLeft();
		CPoint ptHit = m_ptDisplayOrigin + sz;
		OBJLIST hits;
		m_map.HitTest(
			ptHit.x % PIXELS_PER_MAP_IN_WIDTH,
			ptHit.y % PIXELS_PER_MAP_IN_HEIGHT,
			m_viewZMax,
			hits );
		if ( hits.size() > 0 )
		{
			BasicObject* obj = hits.front();
			SHAPEID shpId = obj->ShapeID();

			IregObject* iobj = dynamic_cast<IregObject*>(obj);
			if ( iobj )
			{
				TRACE("OBJ INFO: %s\n", iobj->Name() );
			}
			IregObject* cobj = dynamic_cast<IregObject*>(obj);
			if ( cobj && cobj->IsContainer() )
			{
				std::function<void(IregObject*,int)> recursive =
					[&recursive](IregObject* _c, int lvl) {
						for ( auto o = _c->contained.begin(); o != _c->contained.end(); ++o ) {
							for(int ident=0;ident<=lvl;ident++) TRACE("  ");
							TRACE("%s\n", (*o)->Name());
							if ( dynamic_cast<IregObject*>(*o)
								&& dynamic_cast<IregObject*>(*o)->IsContainer() )
							{
								recursive(dynamic_cast<IregObject*>(*o),lvl+1);
							}
						}
				};

				recursive(cobj,0);				
			}

			m_lcObj.SetItemState(shpId.shapeType,LVIS_SELECTED,LVIS_SELECTED);
			m_lcObj.EnsureVisible(shpId.shapeType,FALSE);
			m_lcObj.SetFocus();

			m_lcShapeFrame.SetItemState(shpId.shapeFrame,LVIS_SELECTED,LVIS_SELECTED);
			m_lcShapeFrame.EnsureVisible(shpId.shapeFrame,FALSE);

			U7VECTOR3 objLocal;
			obj->GetLocalCoords( &objLocal );
			U7VECTOR2 chkCoords;
			obj->GetMapChunk()->GetChunkCoords(&chkCoords);
			CString s;
			for ( int i = 0; i < 3; i++ )
			{
				s.Format(_T("%d"), objLocal.xyz[i]);
				m_lcSelObjInfo.SetItemText( i, 1, s );
			}
			for ( int i = 0; i < 2; i++ )
			{
				s.Format(_T("%d"), chkCoords.xy[i]);
				m_lcSelObjInfo.SetItemText( i + 3, 1, s );
			}

			m_lcSelObjInfo.SetItemText( 6, 1, _T("") );
			// c++ class type
			if ( dynamic_cast<TerrainObject*>(obj) != NULL )
			{
				m_lcSelObjInfo.SetItemText( 5, 1, _T("Terrain") );
			}
			else if ( dynamic_cast<IfixObject*>(obj) != NULL )
			{
				m_lcSelObjInfo.SetItemText( 5, 1, _T("Ifix") );
			}
			else if ( dynamic_cast<IregObject*>(obj) != NULL )
			{
				m_lcSelObjInfo.SetItemText( 5, 1, _T("Ireg") );

				IregObject* iregObj = dynamic_cast<IregObject*>(obj);
				CString s, tmp;
				for ( int i = 0; i < iregObj->info[0]; i++ )
				{
					tmp.Format( _T("%02X "), iregObj->info[i+1] );
					s += tmp;
				}
				m_lcSelObjInfo.SetItemText( 6, 1, s );
				m_lcSelObjInfo.SetColumnWidth(1,LVSCW_AUTOSIZE_USEHEADER);
			}
		}
	}
}


void CU7ObjViewerDlg::OnLButtonUp(UINT nFlags, CPoint point)
{	
	// TODO: Add your message handler code here and/or call default
	__super::OnLButtonUp(nFlags, point);
	if ( m_bLButtonCaptured)
	{
		m_bLButtonCaptured = false;
		ReleaseCapture();
	}
}


void CU7ObjViewerDlg::OnMouseMove(UINT nFlags, CPoint point)
{
	// TODO: Add your message handler code here and/or call default
	__super::OnMouseMove(nFlags, point);
	if ( m_bLButtonCaptured )
	{
		m_CritSection.Lock();
		{
			CSize sz = point - m_ptLastCaptured;
			m_ptNewDisplayOrigin = m_ptLastDisplayOrigin - sz;
			if ( m_ptNewDisplayOrigin.x < 0 )
				m_ptNewDisplayOrigin.x += PIXELS_PER_MAP_IN_WIDTH;
			else if ( m_ptNewDisplayOrigin.x >= PIXELS_PER_MAP_IN_WIDTH )
				m_ptNewDisplayOrigin.x -= PIXELS_PER_MAP_IN_WIDTH;
			if ( m_ptNewDisplayOrigin.y < 0 )
				m_ptNewDisplayOrigin.y += PIXELS_PER_MAP_IN_HEIGHT;
			else if ( m_ptNewDisplayOrigin.y >= PIXELS_PER_MAP_IN_HEIGHT )
				m_ptNewDisplayOrigin.y -= PIXELS_PER_MAP_IN_HEIGHT;
		}
		m_CritSection.Unlock();
	}
	else if ( m_bRButtonCaptured )
	{
		if ( m_selectedObject )
		{
			m_CritSection.Lock();
			{
				CSize sz = point - m_ptLastCaptured;
				if ( abs(sz.cx) >= PIXELS_PER_TILE_IN_WIDTH || abs(sz.cy) >= PIXELS_PER_TILE_IN_HEIGHT )
				{
					int newXTile = m_selCoords.x + sz.cx / PIXELS_PER_TILE_IN_WIDTH;
					int newYTile = m_selCoords.y + sz.cy / PIXELS_PER_TILE_IN_HEIGHT;

					U7VECTOR3 objTileCoords;
					m_selectedObject->GetLocalCoords(&objTileCoords);
					m_selectedObject->SetWorldCoords( newXTile, newYTile, objTileCoords.z );
				}
			}
			m_CritSection.Unlock();
		}
	}
}

void CU7ObjViewerDlg::OnRButtonDown(UINT nFlags, CPoint point)
{
	// TODO: Add your message handler code here and/or call default
	__super::OnRButtonDown(nFlags, point);

	CRect rc;
	m_sttFrame.GetWindowRect(&rc);
	ScreenToClient(&rc);

	if ( rc.PtInRect(point) && !m_bLButtonCaptured )
	{

		CSize sz = point - rc.TopLeft();
		CPoint ptHit = m_ptDisplayOrigin + sz;
		OBJLIST hits;
		m_map.HitTest( ptHit.x, ptHit.y, m_viewZMax, hits );
		if ( hits.size() > 0 )
		{
			BasicObject* obj = hits.front();
			m_selectedObject = obj;
			m_selectedObject->GetWorldCoords(&m_selCoords);

			if ( GetAsyncKeyState(VK_SHIFT) & 0x8000 )
			{
				m_CritSection.Lock();

				// shift key is pressed
				m_selectedObject->GetMapChunk()->Remove(m_selectedObject);
				delete m_selectedObject;
				m_selectedObject = NULL;

				m_CritSection.Unlock();
			}
			else
			{
				SetCapture();
				m_bRButtonCaptured = true;
				m_ptLastCaptured = point;
			}
		}
	}
}


void CU7ObjViewerDlg::OnRButtonUp(UINT nFlags, CPoint point)
{
	// TODO: Add your message handler code here and/or call default
	__super::OnRButtonUp(nFlags, point);
	if ( m_bRButtonCaptured)
	{
		m_selectedObject = NULL;
		m_bRButtonCaptured = false;
		ReleaseCapture();
	}
}

void CU7ObjViewerDlg::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
	// TODO: Add your message handler code here and/or call default
	__super::OnVScroll(nSBCode, nPos, pScrollBar);
	if ( pScrollBar == (CScrollBar*)&m_scZMax )
	{
		m_viewZMax = 13 - m_scZMax.GetPos();
	}
}


void CU7ObjViewerDlg::OnFileExit()
{
	// TODO: Add your command handler code here
	OnBnClickedCancel();
}

static int CALLBACK BrowseCallbackProc(HWND hwnd,UINT uMsg, LPARAM lParam, LPARAM lpData)
{
	// If the BFFM_INITIALIZED message is received
	// set the path to the start path.
	switch (uMsg)
	{
		case BFFM_INITIALIZED:
		{
			if (NULL != lpData)
			{
				SendMessage(hwnd, BFFM_SETSELECTION, TRUE, lpData);
			}
		}
	}

	return 0; // The function should always return 0.
}

// HWND is the parent window.
// szCurrent is an optional start folder. Can be NULL.
// szPath receives the selected path on success. Must be MAX_PATH characters in length.
BOOL BrowseForFolder(HWND hwnd, LPCTSTR szCurrent, LPTSTR szPath)
{
	BROWSEINFO   bi = { 0 };
	LPITEMIDLIST pidl;
	TCHAR        szDisplay[MAX_PATH];
	BOOL         retval;

	CoInitialize(NULL);

	bi.hwndOwner      = hwnd;
	bi.pszDisplayName = szDisplay;
	bi.lpszTitle      = TEXT("Please choose a folder.");
	bi.ulFlags        = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;
	bi.lpfn           = BrowseCallbackProc;
	bi.lParam         = (LPARAM) szCurrent;

	pidl = SHBrowseForFolder(&bi);

	if (NULL != pidl)
	{
		retval = SHGetPathFromIDList(pidl, szPath);
		CoTaskMemFree(pidl);
	}
	else
	{
		retval = FALSE;
	}

	if (!retval)
	{
		szPath[0] = TEXT('\0');
	}

	CoUninitialize();
	return retval;
}

void CU7ObjViewerDlg::OnFileLoad()
{
	// TODO: Add your command handler code here

	TCHAR path[MAX_PATH];
	if ( BrowseForFolder( GetSafeHwnd(), m_strRootPath, path ) )
	{
		m_strRootPath = path;
		if ( m_strRootPath.Right(1) != _T('\\') )
			m_strRootPath += _T('\\');
		AfxGetApp()->WriteProfileString(_T("Settings"),_T("U7RootPath"),m_strRootPath);
	}

}
